/***************************************************************************
 *   Copyright (C) 2008 by Justin L. Boss                                  *
 *   justin@rtechguys.com                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QtGui>
#include <QtOpenGL>
#include <GL/glu.h>
#include <kstandarddirs.h>

#include "ocean.h"
#include "ship.h"
#include "floor.h"
#include "rock.h"
#include "mausoleum.h"
#include "cship.h"
#include "cfloor.h"
#include "crock.h"
#include "cmausoleum.h"

Ocean::Ocean(QWidget *parent) : QGLWidget(QGLFormat(QGL::SampleBuffers), parent)		//QGLFormat(QGL::SampleBuffers|QGL::AlphaChannel|QGL::DirectRendering),
{
    makeCurrent();
    qsrand(QTime::currentTime().msec());
    settings = new Settings();
    startTimer(60);
    SharkTex = LoadImageFile("graynurseshark.png");
    RayTex = LoadImageFile("mania-ray.png");
    GreatWhiteTex = LoadImageFile("greatwhite.png");
    SpadeTex = LoadImageFile("spadefish.png");
    TripletailTex = LoadImageFile("tripletail.png");
    BarracudaTex = LoadImageFile("barracuda.png");
    SailfishTex = LoadImageFile("sailfish.png");
    RockTex = LoadImageFile("rock.png");
    WoodTex = LoadImageFile("wood.png");
    StoneWallTex = LoadImageFile("stone_wall.png");
    SandTex = LoadImageFile("sand.png");
    OrcaTex = LoadImageFile("orca.png");
    TopCaustTex[0] = LoadImageFile ("caust00.png");
    TopCaustTex[1] = LoadImageFile ("caust01.png");
    TopCaustTex[2] = LoadImageFile ("caust02.png");
    TopCaustTex[3] = LoadImageFile ("caust03.png");
    TopCaustTex[4] = LoadImageFile ("caust04.png");
    TopCaustTex[5] = LoadImageFile ("caust05.png");
    TopCaustTex[6] = LoadImageFile ("caust06.png");
    TopCaustTex[7] = LoadImageFile ("caust07.png");
    TopCaustTex[8] = LoadImageFile ("caust08.png");
    TopCaustTex[9] = LoadImageFile ("caust09.png");
    TopCaustTex[10] = LoadImageFile("caust10.png");
    TopCaustTex[11] = LoadImageFile("caust11.png");
    TopCaustTex[12] = LoadImageFile("caust12.png");
    TopCaustTex[13] = LoadImageFile("caust13.png");
    TopCaustTex[14] = LoadImageFile("caust14.png");
    TopCaustTex[15] = LoadImageFile("caust15.png");
    TopCaustTex[16] = LoadImageFile("caust16.png");
    TopCaustTex[17] = LoadImageFile("caust17.png");
    TopCaustTex[18] = LoadImageFile("caust18.png");
    TopCaustTex[19] = LoadImageFile("caust19.png");
    TopCaustTex[20] = LoadImageFile("caust20.png");
    TopCaustTex[21] = LoadImageFile("caust21.png");
    TopCaustTex[22] = LoadImageFile("caust22.png");
    TopCaustTex[23] = LoadImageFile("caust23.png");
    TopCaustTex[24] = LoadImageFile("caust24.png");
    TopCaustTex[25] = LoadImageFile("caust25.png");
    TopCaustTex[26] = LoadImageFile("caust26.png");
    TopCaustTex[27] = LoadImageFile("caust27.png");
    TopCaustTex[28] = LoadImageFile("caust28.png");
    TopCaustTex[29] = LoadImageFile("caust29.png");
    TopCaustTex[30] = LoadImageFile("caust30.png");
    TopCaustTex[31] = LoadImageFile("caust31.png");
    BtmCaustTex[0] = LoadImageFile ("Caust00.png");
    BtmCaustTex[1] = LoadImageFile ("Caust01.png");
    BtmCaustTex[2] = LoadImageFile ("Caust02.png");
    BtmCaustTex[3] = LoadImageFile ("Caust03.png");
    BtmCaustTex[4] = LoadImageFile ("Caust04.png");
    BtmCaustTex[5] = LoadImageFile ("Caust05.png");
    BtmCaustTex[6] = LoadImageFile ("Caust06.png");
    BtmCaustTex[7] = LoadImageFile ("Caust07.png");
    BtmCaustTex[8] = LoadImageFile ("Caust08.png");
    BtmCaustTex[9] = LoadImageFile ("Caust09.png");
    BtmCaustTex[10] = LoadImageFile("Caust10.png");
    BtmCaustTex[11] = LoadImageFile("Caust11.png");
    BtmCaustTex[12] = LoadImageFile("Caust12.png");
    BtmCaustTex[13] = LoadImageFile("Caust13.png");
    BtmCaustTex[14] = LoadImageFile("Caust14.png");
    BtmCaustTex[15] = LoadImageFile("Caust15.png");
    BtmCaustTex[16] = LoadImageFile("Caust16.png");
    BtmCaustTex[17] = LoadImageFile("Caust17.png");
    BtmCaustTex[18] = LoadImageFile("Caust18.png");
    BtmCaustTex[19] = LoadImageFile("Caust19.png");
    BtmCaustTex[20] = LoadImageFile("Caust20.png");
    BtmCaustTex[21] = LoadImageFile("Caust21.png");
    BtmCaustTex[22] = LoadImageFile("Caust22.png");
    BtmCaustTex[23] = LoadImageFile("Caust23.png");
    BtmCaustTex[24] = LoadImageFile("Caust24.png");
    BtmCaustTex[25] = LoadImageFile("Caust25.png");
    BtmCaustTex[26] = LoadImageFile("Caust26.png");
    BtmCaustTex[27] = LoadImageFile("Caust27.png");
    BtmCaustTex[28] = LoadImageFile("Caust28.png");
    BtmCaustTex[29] = LoadImageFile("Caust29.png");
    BtmCaustTex[30] = LoadImageFile("Caust30.png");
    BtmCaustTex[31] = LoadImageFile("Caust31.png");
    CaustCounter=0;
    setWindowTitle(tr("KDE Ocean Screen Saver"));

    orca = new SeaCreature *[settings->Orca_Count];
    for (int x=0;x<settings->Orca_Count;x++)
    {
        orca[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "orca.agl");
        orca[x]->SetTexture (OrcaTex);
        orca[x]->SetAgility(settings->Orca_Agility);
        orca[x]->SetLocation (qrand() % settings->Orca_Far - (settings->Orca_Far/2), settings->Orca_Small*x*0.1-settings->Orca_Depth, -x*settings->Orca_Far);
        orca[x]->SetDestination(qrand() % settings->Orca_Far - (settings->Orca_Far/2), 0.0f, -qrand() % (settings->Orca_Far - settings->Orca_Near) - settings->Orca_Near);
        orca[x]->SetSize(qrand() % (settings->Orca_Larg - settings->Orca_Small) + settings->Orca_Small);
        orca[x]->SetTailSpan(settings->Orca_TMove);
        orca[x]->Mammal = true;
    }

    shark = new SeaCreature *[settings->LS_Count];
    for (int x=0;x<settings->LS_Count;x++)
    {
        shark[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "graynurseshark.agl");
        shark[x]->SetTexture (SharkTex);
        shark[x]->SetAgility(settings->LS_Agility);
        shark[x]->SetLocation (qrand() % settings->LS_Far - (settings->LS_Far/2), settings->LS_Small*x*0.1-settings->LS_Depth, -x*settings->LS_Far);
        shark[x]->SetDestination(qrand() % settings->LS_Far - (settings->LS_Far/2), 0.0f, -qrand() % (settings->LS_Far - settings->LS_Near) - settings->LS_Near);
        shark[x]->SetSize(qrand() % (settings->LS_Larg - settings->LS_Small) + settings->LS_Small);
        shark[x]->SetTailSpan(settings->LS_TMove);
    }

    greatwhite = new SeaCreature *[settings->GW_Count];
    for (int x=0;x<settings->GW_Count;x++)
    {
        greatwhite[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "greatwhite.agl");
        greatwhite[x]->SetTexture (GreatWhiteTex);
        greatwhite[x]->SetAgility(settings->GW_Agility);
        greatwhite[x]->SetLocation (qrand() % settings->GW_Far - (settings->GW_Far/2), settings->GW_Small*x*0.1-settings->GW_Depth, -x*settings->GW_Far);
        greatwhite[x]->SetDestination(qrand() % settings->GW_Far - (settings->GW_Far/2), 0.0f, -qrand() % (settings->GW_Far - settings->GW_Near) - settings->GW_Near);
        greatwhite[x]->SetSize(qrand() % (settings->GW_Larg - settings->GW_Small) + settings->GW_Small);
        greatwhite[x]->SetTailSpan(settings->GW_TMove);
    }

    barracuda = new SeaCreature *[settings->BC_Count];
    for (int x=0;x<settings->BC_Count;x++)
    {
        barracuda[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "barracuda.agl");
        barracuda[x]->SetTexture (BarracudaTex);
        barracuda[x]->SetAgility(settings->BC_Agility);
        barracuda[x]->SetLocation (qrand() % settings->BC_Far - (settings->BC_Far/2), settings->BC_Small*x*0.1-settings->BC_Depth, -x*settings->BC_Far);
        barracuda[x]->SetDestination(qrand() % settings->BC_Far - (settings->BC_Far/2), 0.0f, -qrand() % (settings->BC_Far - settings->BC_Near) - settings->BC_Near);
        barracuda[x]->SetSize(qrand() % (settings->BC_Larg - settings->BC_Small) + settings->BC_Small);
        barracuda[x]->SetTailSpan(settings->BC_TMove);
    }

    sailfish = new SeaCreature *[settings->Sail_Count];
    for (int x=0;x<settings->Sail_Count;x++)
    {
        sailfish[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "sailfish.agl");
        sailfish[x]->SetTexture (SailfishTex);
        sailfish[x]->SetAgility(settings->Sail_Agility);
        sailfish[x]->SetLocation (qrand() % settings->Sail_Far - (settings->Sail_Far/2), settings->Sail_Small*x*0.1-settings->Sail_Depth, -x*settings->Sail_Far);
        sailfish[x]->SetDestination(qrand() % settings->Sail_Far - (settings->Sail_Far/2), 0.0f, -qrand() % (settings->Sail_Far - settings->Sail_Near) - settings->Sail_Near);
        sailfish[x]->SetSize(qrand() % (settings->Sail_Larg - settings->Sail_Small) + settings->Sail_Small);
        sailfish[x]->SetTailSpan(settings->Sail_TMove);
    }

    spade = new SeaCreature *[settings->Spade_Count];
    for (int x=0;x<settings->Spade_Count;x++)
    {
        spade[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "spadefish.agl");
        spade[x]->SetTexture (SpadeTex);
        spade[x]->SetAgility(settings->Spade_Agility);
        spade[x]->SetLocation (qrand() % settings->Spade_Far - (settings->Spade_Far/2), settings->Spade_Small*x*0.1-settings->Spade_Depth, -x*settings->Spade_Far);
        spade[x]->SetDestination(qrand() % settings->Spade_Far - (settings->Spade_Far/2), 0.0f, -qrand() % (settings->Spade_Far - settings->Spade_Near) - settings->Spade_Near);
        spade[x]->SetSize(qrand() % (settings->Spade_Larg - settings->Spade_Small) + settings->Spade_Small);
        spade[x]->SetTailSpan(settings->Spade_TMove);
    }

    tripletail = new SeaCreature *[settings->Tripletail_Count];
    for (int x=0;x<settings->Tripletail_Count;x++)
    {
        tripletail[x] = new SeaCreature (KStandardDirs::locate("data", QString("koceansaver/data/")).toAscii() + "tripletail.agl");
        tripletail[x]->SetTexture (TripletailTex);
        tripletail[x]->SetAgility(settings->Tripletail_Agility);
        tripletail[x]->SetLocation (qrand() % settings->Tripletail_Far - (settings->Tripletail_Far/2), settings->Tripletail_Small*x*0.1-settings->Tripletail_Depth, -x*settings->Tripletail_Far);
        tripletail[x]->SetDestination(qrand() % settings->Tripletail_Far - (settings->Tripletail_Far/2), 0.0f, -qrand() % (settings->Tripletail_Far - settings->Tripletail_Near) - settings->Tripletail_Near);
        tripletail[x]->SetSize(qrand() % (settings->Tripletail_Larg - settings->Tripletail_Small) + settings->Tripletail_Small);
        tripletail[x]->SetTailSpan(settings->Tripletail_TMove);
    }

    if (settings->WaveEnabled)
    {
        wave = new WaveMap(settings->WaveWidth, settings->WaveDepth, 8.0, 8.0, 0.5, 0.0, 999 - settings->WaveSpeed);
        wave->DisturbWave(settings->WaveWidth / 3, settings->WaveDepth / 2, settings->WaveWidth / 5, settings->WaveDepth / 5, settings->WaveSize * 0.01);
                wave->UpdateWave(1000);
    }
}

Ocean::~Ocean()
{
    for (int x=0;x<settings->LS_Count;x++)
        delete orca[x];

    for (int x=0;x<settings->LS_Count;x++)
        delete shark[x];

    for (int x=0;x<settings->GW_Count;x++)
        delete greatwhite[x];

    for (int x=0;x<settings->BC_Count;x++)
        delete barracuda[x];

    for (int x=0;x<settings->Sail_Count;x++)
        delete sailfish[x];

    for (int x=0;x<settings->Spade_Count;x++)
        delete spade[x];

    for (int x=0;x<settings->Tripletail_Count;x++)
        delete tripletail[x];

    if (settings->WaveEnabled)
        delete wave;
    makeCurrent();
}

void Ocean::initializeGL()
{
    glEnable (GL_TEXTURE_2D);
    glShadeModel (GL_SMOOTH);                                                                   // Enable smooth shading
    if (settings->SampleBuffer)
        glEnable(GL_MULTISAMPLE);
    else
        glDisable(GL_MULTISAMPLE);

    glClearColor ((GLfloat)settings->Color.red()/256,
                  (GLfloat)settings->Color.green()/256,
                  (GLfloat)settings->Color.blue()/256,
                  1.0f);                                                                        // Set the background black

    glClearDepth (1.0);		// Depth buffer setup
    glEnable (GL_DEPTH_TEST);                                                                   // Enables Depth Testing
    glDepthFunc (GL_LEQUAL);                                                                    // The Type Of Depth Test To Do
    glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);                                         // Really Nice Perspective Calculations

    GLfloat LightAmbient[]={(GLfloat)settings->Ambient * 0.1f,
                                  (GLfloat)settings->Ambient * 0.1f,
                                  (GLfloat)settings->Ambient * 0.1f,
                                  (GLfloat)settings->Ambient * 0.01};
    glLightfv (GL_LIGHT1, GL_AMBIENT, LightAmbient);                                            // Setup The Ambient Light

    GLfloat LightDiffuse[]={1.0f,
                            1.0f,
                            1.0f,
                            (GLfloat)settings->Light * 0.01f };
    glLightfv (GL_LIGHT1, GL_DIFFUSE, LightDiffuse);                                            // Setup The Diffuse Light

    GLfloat LightPosition[]={ 0.0f, 100.0f, 2.0f, 1.0f };
    glLightfv (GL_LIGHT1, GL_POSITION, LightPosition);                                          // Position The Light

    glEnable (GL_LIGHT1);
    glEnable (GL_LIGHTING);

    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glFogi (GL_FOG_MODE, GL_EXP2);                                                              // Fog Mode GL_EXP, GL_EXP2, GL_LINEAR

    GLfloat fogColor[3] = {(GLfloat)settings->Color.red()/256,
                           (GLfloat)settings->Color.green()/256,
                           (GLfloat)settings->Color.blue()/256};                                // Fog Color
    glFogfv (GL_FOG_COLOR,fogColor);                                                            // Set Fog Color

    glFogf (GL_FOG_DENSITY, (GLfloat)settings->Clarity* 0.001f);                                // How Dense Will The Fog Be
    glHint (GL_FOG_HINT, GL_DONT_CARE);                                                         // Fog Hint Value
    glFogf (GL_FOG_START, 1.0f);                                                                // Fog Start Depth
    glFogf (GL_FOG_END, 90.0f);                                                                 // Fog End Depth
    glEnable (GL_FOG);                                                                          // Enables GL_FOG
}

void Ocean::paintGL()
{
    GLfloat rocking;
    if (settings->RockingEnabled)
        rocking = settings->Amplitude * sin(settings->Frequency * CaustCounter);
    else
        rocking = 0.0;

    glPushMatrix();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glColor4f ((float)settings->Color.red()/256,(float)settings->Color.green()/256,(float)settings->Color.blue()/256,settings->CausticsFloor*0.01);

    glLoadIdentity();

    if (settings->WaveEnabled)
    {
        glTranslatef (0.0, rocking + 6, -12.0);
        glBindTexture (GL_TEXTURE_2D, TopCaustTex[CaustCounter%32]);
        glDisable (GL_LIGHTING);
        glEnable (GL_BLEND);
        wave->Render();
        glDisable (GL_BLEND);
        glEnable (GL_LIGHTING);
    }

    if (settings->RockEnabled)
    {
        glLoadIdentity();
        glTranslatef (0.0,  rocking, 0.0);
        Rock(RockTex);
        glDisable (GL_LIGHTING);
        glEnable (GL_BLEND);
        cRock(BtmCaustTex[CaustCounter%32]);
        glEnable (GL_LIGHTING);
        glDisable (GL_BLEND);
    }

    if (settings->MausoleumEnabled)
    {
        glLoadIdentity();
        glTranslatef (0.0, rocking, 0.0);
        Mausoleum(RockTex, StoneWallTex);
        glDisable (GL_LIGHTING);
        glEnable (GL_BLEND);
        cMausoleum(BtmCaustTex[CaustCounter%32]);
        glEnable (GL_LIGHTING);
        glDisable (GL_BLEND);
    }

    if (settings->ShipEnabled)
    {
        glLoadIdentity();
        glTranslatef (0.0, rocking, 0.0);
        Ship(WoodTex);
        glDisable (GL_LIGHTING);
        glEnable (GL_BLEND);
        cShip(BtmCaustTex[CaustCounter%32]);
        glEnable (GL_LIGHTING);
        glDisable (GL_BLEND);
    }

    if (settings->SeaFloorEnabled)
    {
        glLoadIdentity();
        glTranslatef (0.0, rocking, 0.0);
        SeaFloor(SandTex);
        glDisable (GL_LIGHTING);
        glEnable (GL_BLEND);
        cSeaFloor(BtmCaustTex[CaustCounter%32]);
        glEnable (GL_LIGHTING);
        glDisable (GL_BLEND);
    }

    glLoadIdentity();

    for (int x=0; x<settings->Orca_Count; x++)
    {
        orca[x]->Draw (orca[x]->Size * 0.5, settings->Orca_Speed, rocking);
        if (orca[x]->Arrived)
            orca[x]->SetDestination(qrand() % settings->Orca_Far - (settings->Orca_Far/2), 0.0f, -qrand() % (settings->Orca_Far - settings->Orca_Near) - settings->Orca_Near);
    }

    for (int x=0; x<settings->LS_Count; x++)
    {
        shark[x]->Draw (shark[x]->Size * 0.5, settings->LS_Speed, rocking);
        if (shark[x]->Arrived)
            shark[x]->SetDestination(qrand() % settings->LS_Far - (settings->LS_Far/2), 0.0f, -qrand() % (settings->LS_Far - settings->LS_Near) - settings->LS_Near);
    }

    for (int x=0; x<settings->GW_Count; x++)
    {
        greatwhite[x]->Draw (greatwhite[x]->Size * 0.5, settings->GW_Speed, rocking);
        if (greatwhite[x]->Arrived)
            greatwhite[x]->SetDestination(qrand() % settings->GW_Far - (settings->GW_Far/2), 0.0f, -qrand() % (settings->GW_Far - settings->GW_Near) - settings->GW_Near);
    }

    for (int x=0; x<settings->BC_Count; x++)
    {
        barracuda[x]->Draw (barracuda[x]->Size * 0.5, settings->BC_Speed, rocking);
        if (barracuda[x]->Arrived)
            barracuda[x]->SetDestination(qrand() % settings->BC_Far - (settings->BC_Far/2), 0.0f, -qrand() % (settings->BC_Far - settings->BC_Near) - settings->BC_Near);
    }

    for (int x=0; x<settings->Sail_Count; x++)
    {
        sailfish[x]->Draw (sailfish[x]->Size * 0.5, settings->Sail_Speed, rocking);
        if (sailfish[x]->Arrived)
            sailfish[x]->SetDestination(qrand() % settings->Sail_Far - (settings->Sail_Far/2), 0.0f, -qrand() % (settings->Sail_Far - settings->Sail_Near) - settings->Sail_Near);
    }

    for (int x=0; x<settings->Spade_Count; x++)
    {
        spade[x]->Draw (spade[x]->Size * 0.5, settings->Spade_Speed, rocking);
        if (spade[x]->Arrived)
            spade[x]->SetDestination(qrand() % settings->Spade_Far - (settings->Spade_Far/2), 0.0f, -qrand() % (settings->Spade_Far - settings->Spade_Near) - settings->Spade_Near);
    }

    for (int x=0; x<settings->Tripletail_Count; x++)
    {
        tripletail[x]->Draw (tripletail[x]->Size * 0.5, settings->Tripletail_Speed, rocking);
        if (tripletail[x]->Arrived)
            tripletail[x]->SetDestination(qrand() % settings->Tripletail_Far - (settings->Tripletail_Far/2), 0.0f, -qrand() % (settings->Tripletail_Far - settings->Tripletail_Near) - settings->Tripletail_Near);
    }

    CaustCounter++;
    glPopMatrix();
}

void Ocean::resizeGL(GLint width, GLint height)
{
    glViewport (0, 0, width, height);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45.0, GLfloat (width) / GLfloat (height), 0.5, 50.0);
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity();
}

GLint Ocean::LoadImageFile(const char* ImageFile)
{
    QImage Image;
        GLuint Texture = 0;
    if (Image.load(KStandardDirs::locate("data", QString("koceansaver/data/") + ImageFile)))
        {
            Image = QGLWidget::convertToGLFormat(Image);
            glGenTextures(1, &Texture);
            glBindTexture(GL_TEXTURE_2D, Texture);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Image.width(), Image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image.bits());
        }
    return Texture;
}
